home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Libraries / Apache 1.0 / src / mod_log_config.c < prev    next >
Text File  |  1995-12-04  |  16KB  |  560 lines

  1.  
  2. /* ====================================================================
  3.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  *
  9.  * 1. Redistributions of source code must retain the above copyright
  10.  *    notice, this list of conditions and the following disclaimer. 
  11.  *
  12.  * 2. Redistributions in binary form must reproduce the above copyright
  13.  *    notice, this list of conditions and the following disclaimer in
  14.  *    the documentation and/or other materials provided with the
  15.  *    distribution.
  16.  *
  17.  * 3. All advertising materials mentioning features or use of this
  18.  *    software must display the following acknowledgment:
  19.  *    "This product includes software developed by the Apache Group
  20.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  21.  *
  22.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  23.  *    endorse or promote products derived from this software without
  24.  *    prior written permission.
  25.  *
  26.  * 5. Redistributions of any form whatsoever must retain the following
  27.  *    acknowledgment:
  28.  *    "This product includes software developed by the Apache Group
  29.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  30.  *
  31.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  32.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  33.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  35.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  39.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  42.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  43.  * ====================================================================
  44.  *
  45.  * This software consists of voluntary contributions made by many
  46.  * individuals on behalf of the Apache Group and was originally based
  47.  * on public domain software written at the National Center for
  48.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  49.  * For more information on the Apache Group and the Apache HTTP server
  50.  * project, please see <http://www.apache.org/>.
  51.  *
  52.  */
  53.  
  54.  
  55. /*
  56.  * This is an EXPERIMENTAL module, which implements the TransferLog directive
  57.  * (same as the common log module), and an additional directive, LogFormat.
  58.  * Bugs would not surprise me.
  59.  *
  60.  * The argument to LogFormat is a string, which can include literal
  61.  * characters copied into the log files, and '%' directives as follows:
  62.  *
  63.  * %...h:  remote host
  64.  * %...l:  remote logname (from identd, if supplied)
  65.  * %...u:  remote user (from auth; may be bogus if return status (%s) is 401)
  66.  * %...t:  time, in common log format time format
  67.  * %...r:  first line of request
  68.  * %...s:  status.  For requests that got internally redirected, this
  69.  *         is status of the *original* request --- %...>s for the last.
  70.  * %...b:  bytes sent.
  71.  * %...{Foobar}i:  The contents of Foobar: header line(s) in the request
  72.  *                 sent to the client.
  73.  * %...{Foobar}o:  The contents of Foobar: header line(s) in the reply.
  74.  *
  75.  * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
  76.  * indicate conditions for inclusion of the item (which will cause it
  77.  * to be replaced with '-' if the condition is not met).  Note that
  78.  * there is no escaping performed on the strings from %r, %...i and
  79.  * %...o; some with long memories may remember that I thought this was
  80.  * a bad idea, once upon a time, and I'm still not comfortable with
  81.  * it, but it is difficult to see how to "do the right thing" with all
  82.  * of '%..i', unless we URL-escape everything and break with CLF.
  83.  *
  84.  * The forms of condition are a list of HTTP status codes, which may
  85.  * or may not be preceded by '!'.  Thus, '%400,501{User-agent}i' logs
  86.  * User-agent: on 400 errors and 501 errors (Bad Request, Not
  87.  * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
  88.  * requests which did *not* return some sort of normal status.
  89.  *
  90.  * The default LogFormat reproduces CLF; see below.
  91.  *
  92.  * The way this is supposed to work with virtual hosts is as follows:
  93.  * a virtual host can have its own LogFormat, or its own TransferLog.
  94.  * If it doesn't have its own LogFormat, it inherits from the main
  95.  * server.  If it doesn't have its own TransferLog, it writes to the
  96.  * same descriptor (meaning the same process for "| ...").
  97.  *
  98.  * That means that you can do things like:
  99.  *
  100.  * <VirtualHost hosta.com>
  101.  * LogFormat "hosta ..."
  102.  * ...
  103.  * </VirtualHost>
  104.  *
  105.  * <VirtualHost hosta.com>
  106.  * LogFormat "hostb ..."
  107.  * ...
  108.  * </VirtualHost>
  109.  *
  110.  * ... to have different virtual servers write into the same log file,
  111.  * but have some indication which host they came from, though a %v
  112.  * directive may well be a better way to handle this.
  113.  *
  114.  * --- rst
  115.  */
  116.  
  117. #define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %s %b"
  118.  
  119. #include "httpd.h"
  120. #include "http_config.h"
  121.  
  122. module config_log_module;
  123.  
  124. static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT );
  125. static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
  126.  
  127. typedef struct {
  128.     char *fname;
  129.     array_header *format;
  130.     int log_fd;
  131. } config_log_state;
  132.  
  133. /*
  134.  * Format items...
  135.  */
  136.  
  137. typedef char *(*item_key_func)(request_rec *, char *);
  138.  
  139. typedef struct {
  140.     item_key_func func;
  141.     char *arg;
  142.     int condition_sense;
  143.     int want_orig;
  144.     array_header *conditions;
  145. } log_format_item;
  146.  
  147. char *format_integer(pool *p, int i)
  148. {
  149.     char dummy[40];
  150.     sprintf (dummy, "%d", i);
  151.     return pstrdup (p, dummy);
  152. }
  153.  
  154. static char *pfmt(pool *p, int i)
  155. {
  156.     if (i <= 0) return "-";
  157.     else return format_integer (p, i);
  158. }
  159.  
  160. char *constant_item (request_rec *dummy, char *stuff) { return stuff; }
  161.  
  162. char *log_remote_host (request_rec *r, char *a)
  163. { return r->connection->remote_name; }
  164.  
  165. char *log_remote_logname(request_rec *r, char *a)
  166. {return r->connection->remote_logname;}
  167.  
  168. char *log_remote_user (request_rec *r, char *a)
  169. { return r->connection->user; }
  170.  
  171. char *log_request_line (request_rec *r, char *a)
  172. { return r->the_request; }
  173.  
  174. char *log_status (request_rec *r, char *a)
  175. { return pfmt(r->pool, r->status); }
  176.  
  177. char *log_bytes_sent (request_rec *r, char *a)
  178. { return pfmt (r->pool, r->bytes_sent); }
  179.  
  180. char *log_header_in (request_rec *r, char *a)
  181. { return table_get (r->headers_in, a); }
  182.  
  183. char *log_header_out (request_rec *r, char *a)
  184. {
  185.     char *cp = table_get (r->headers_out, a);
  186.     if (cp) return cp;
  187.     return table_get (r->err_headers_out, a);
  188. }
  189.  
  190. char *log_env_var (request_rec *r, char *a)
  191. { return table_get (r->subprocess_env, a); }
  192.  
  193. char *log_request_time (request_rec *r, char *a)
  194. {
  195.     long timz;
  196.     struct tm *t;
  197.     char tstr[MAX_STRING_LEN],sign;
  198.     
  199.     t = get_gmtoff(&timz);
  200.     sign = (timz < 0 ? '-' : '+');
  201.     if(timz < 0) 
  202.         timz = -timz;
  203.  
  204.     strftime(tstr,MAX_STRING_LEN,"[%d/%b/%Y:%H:%M:%S ",t);
  205.  
  206.     sprintf (tstr + strlen(tstr), "%c%02ld%02ld]",
  207.          sign, timz/3600, timz%3600);
  208.  
  209.     return pstrdup (r->pool, tstr);
  210. }
  211.  
  212. /*****************************************************************
  213.  *
  214.  * Parsing the log format string
  215.  */
  216.  
  217. struct log_item_list {
  218.     char ch;
  219.     item_key_func func;
  220.     int want_orig_default;
  221. } log_item_keys[] = {
  222.     { 'h', log_remote_host, 0 },
  223.     { 'l', log_remote_logname, 0 },
  224.     { 'u', log_remote_user, 0 },
  225.     { 't', log_request_time, 0 },
  226.     { 'r', log_request_line, 1 },
  227.     { 's', log_status, 1 },
  228.     { 'b', log_bytes_sent, 0 },
  229.     { 'i', log_header_in, 0 },
  230.     { 'o', log_header_out, 0 },
  231.     { 'e', log_env_var, 0 },
  232.     { '\0' }
  233. };
  234.  
  235. struct log_item_list  *find_log_func (char k)
  236. {
  237.     int i;
  238.  
  239.     for (i = 0; log_item_keys[i].ch; ++i)
  240.     if (k == log_item_keys[i].ch)
  241.         return &log_item_keys[i];
  242.  
  243.     return NULL;
  244. }
  245.  
  246. char *log_format_substring (pool *p, char *start, char *end)
  247. {
  248.     char *res = palloc (p, end - start + 1);
  249.     strncpy (res, start, end - start);
  250.     res[end - start] = '\0';
  251.     return res;
  252. }
  253.  
  254. char *parse_log_misc_string (pool *p, log_format_item *it, char **sa)
  255. {
  256.     char *s = *sa;
  257.     
  258.     it->func = constant_item;
  259.     it->conditions = NULL;
  260.  
  261.     while (*s && *s != '%') ++s;
  262.     it->arg = log_format_substring (p, *sa, s);
  263.     *sa = s;
  264.     
  265.     return NULL;
  266. }
  267.  
  268. char *parse_log_item (pool *p, log_format_item *it, char **sa)
  269. {
  270.     char *s = *sa;
  271.     if (*s != '%') return parse_log_misc_string (p, it, sa);
  272.  
  273.     ++s;
  274.     it->condition_sense = 0;
  275.     it->conditions = NULL;
  276.     it->want_orig = -1;
  277.     it->arg = "";        /* For safety's sake... */
  278.  
  279.     while (*s) {
  280.     int i;
  281.     struct log_item_list *l;
  282.     
  283.     switch (*s) {
  284.     case '!':
  285.         ++s;
  286.         it->condition_sense = !it->condition_sense;
  287.         break;
  288.  
  289.     case '<':
  290.         ++s;
  291.         it->want_orig = 1;
  292.         break;
  293.  
  294.     case '>':
  295.         ++s;
  296.         it->want_orig = 0;
  297.         break;
  298.         
  299.     case ',':
  300.         ++s;
  301.         break;
  302.  
  303.     case '{':
  304.         ++s;
  305.         it->arg = getword (p, &s, '}');
  306.         break;
  307.         
  308.     case '0': case '1': case '2': case '3': case '4': 
  309.     case '5': case '6': case '7': case '8': case '9':
  310.         i = *s - '0';
  311.         while (isdigit (*++s)) i = i * 10 + (*s) - '0';
  312.         if (!it->conditions)
  313.         it->conditions = make_array (p, 4, sizeof(int));
  314.         *(int *)push_array(it->conditions) = i;
  315.         break;
  316.  
  317.     default:
  318.         l = find_log_func (*s++);
  319.         if (!l) {
  320.         char dummy[] = { '\0', '\0'};
  321.         dummy[0] = s[-1];
  322.         return pstrcat (p, "Unrecognized LogFormat directive %",
  323.                 dummy, NULL);
  324.         }
  325.         it->func = l->func;
  326.         if (it->want_orig == -1) it->want_orig = l->want_orig_default;
  327.         *sa = s;
  328.         return NULL;
  329.     }
  330.     }
  331.  
  332.     return "Ran off end of LogFormat parsing args to some directive";
  333. }
  334.  
  335. array_header *parse_log_string (pool *p, char *s, char **err)
  336. {
  337.     array_header *a = make_array (p, 30, sizeof (log_format_item));
  338.     char *res;
  339.  
  340.     while (*s) {
  341.     if ((res = parse_log_item (p, (log_format_item *)push_array(a), &s))) {
  342.         *err = res;
  343.         return NULL;
  344.     }
  345.     }
  346.  
  347.     s = "\n";
  348.     parse_log_item (p, (log_format_item *)push_array(a), &s);
  349.     return a;
  350. }
  351.  
  352. /*****************************************************************
  353.  *
  354.  * Actually logging.
  355.  */
  356.  
  357. char *process_item(request_rec *r, request_rec *orig, log_format_item *item)
  358. {
  359.     char *cp;
  360.     
  361.     /* First, see if we need to process this thing at all... */
  362.  
  363.     if (item->conditions && item->conditions->nelts != 0) {
  364.     int i;
  365.     int *conds = (int *)item->conditions->elts;
  366.     int in_list = 0;
  367.  
  368.     for (i = 0; i < item->conditions->nelts; ++i)
  369.         if (r->status == conds[i]) {
  370.         in_list = 1;
  371.         break;
  372.         }
  373.  
  374.     if ((item->condition_sense && in_list)
  375.         || (!item->condition_sense && !in_list))
  376.     {
  377.         return "-";
  378.     }
  379.     }
  380.  
  381.     /* We do.  Do it... */
  382.  
  383.     cp = (*item->func)(item->want_orig ? orig : r, item->arg);
  384.     return cp ? cp : "-";
  385. }
  386.  
  387. int config_log_transaction(request_rec *r)
  388. {
  389.     config_log_state *cls = get_module_config (r->server->module_config,
  390.                            &config_log_module);
  391.   
  392.     array_header *strsa= make_array(r->pool, cls->format->nelts,sizeof(char*));
  393.     log_format_item *items = (log_format_item *)cls->format->elts;
  394.     char *str, **strs, *s;
  395.     request_rec *orig;
  396.     int i;
  397.     int len = 0;
  398.  
  399.     orig = r;
  400.     while (orig->prev) orig = orig->prev;
  401.     while (r->next) r = r->next;
  402.  
  403.     for (i = 0; i < cls->format->nelts; ++i)
  404.     *((char**)push_array (strsa)) = process_item (r, orig, &items[i]);
  405.  
  406.     strs = (char **)strsa->elts;
  407.     
  408.     for (i = 0; i < cls->format->nelts; ++i)
  409.     len += strlen (strs[i]);
  410.  
  411.     str = palloc (r->pool, len + 1);
  412.  
  413.     for (i = 0, s = str; i < cls->format->nelts; ++i) {
  414.     strcpy (s, strs[i]);
  415.     s += strlen (strs[i]);
  416.     }
  417.     
  418.     write(cls->log_fd, str, strlen(str));
  419.  
  420.     return OK;
  421. }
  422.  
  423. /*****************************************************************
  424.  *
  425.  * Module glue...
  426.  */
  427.  
  428. void *make_config_log_state (pool *p, server_rec *s)
  429. {
  430.     config_log_state *cls =
  431.       (config_log_state *)palloc (p, sizeof (config_log_state));
  432.  
  433.     cls->fname = NULL;
  434.     cls->format = NULL;
  435.     cls->log_fd = -1;
  436.  
  437.     return (void *)cls;
  438. }
  439.  
  440. char *set_config_log (cmd_parms *parms, void *dummy, char *arg)
  441. {
  442.     config_log_state *cls = get_module_config (parms->server->module_config,
  443.                            &config_log_module);
  444.   
  445.     cls->fname = arg;
  446.     return NULL;
  447. }
  448.  
  449. char *log_format (cmd_parms *cmd, void *dummy, char *arg)
  450. {
  451.     char *err_string = NULL;
  452.     config_log_state *cls = get_module_config (cmd->server->module_config,
  453.                            &config_log_module);
  454.   
  455.     cls->format = parse_log_string (cmd->pool, arg, &err_string);
  456.     return err_string;
  457. }
  458.  
  459. command_rec config_log_cmds[] = {
  460. { "TransferLog", set_config_log, NULL, RSRC_CONF, TAKE1,
  461.     "the filename of the access log" },
  462. { "LogFormat", log_format, NULL, RSRC_CONF, TAKE1,
  463.       "a log format string (see docs)" },
  464. { NULL }
  465. };
  466.  
  467. void config_log_child (void *cmd)
  468. {
  469.     /* Child process code for 'TransferLog "|..."';
  470.      * may want a common framework for this, since I expect it will
  471.      * be common for other foo-loggers to want this sort of thing...
  472.      */
  473.     
  474.     cleanup_for_exec();
  475.     signal (SIGHUP, SIG_IGN);
  476.     execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
  477.     fprintf (stderr, "Exec of shell for logging failed!!!\n");
  478.     exit (1);
  479. }
  480.  
  481. config_log_state *open_config_log (server_rec *s, pool *p,
  482.                    config_log_state *defaults)
  483. {
  484.     config_log_state *cls = get_module_config (s->module_config,
  485.                            &config_log_module);
  486.   
  487.     if (cls->log_fd > 0) return cls; /* virtual config shared w/main server */
  488.     
  489.     if (cls->format == NULL) {
  490.     char *dummy;
  491.     
  492.     if (defaults) cls->format = defaults->format;
  493.     else cls->format = parse_log_string (p, DEFAULT_LOG_FORMAT, &dummy);
  494.     }
  495.  
  496.     if (cls->fname == NULL) {
  497.     if (defaults) {
  498.         cls->log_fd = defaults->log_fd;
  499.         return cls;
  500.     }
  501.     else cls->fname = DEFAULT_XFERLOG;
  502.     }
  503.     
  504.     if (*cls->fname == '|') {
  505.     FILE *dummy;
  506.     
  507.     spawn_child(p, config_log_child, (void *)(cls->fname+1),
  508.             kill_after_timeout, &dummy, NULL);
  509.  
  510.     if (dummy == NULL) {
  511.         fprintf (stderr, "Couldn't fork child for TransferLog process\n");
  512.         exit (1);
  513.     }
  514.  
  515.     cls->log_fd = fileno (dummy);
  516.     }
  517.     else {
  518.     char *fname = server_root_relative (p, cls->fname);
  519.     if((cls->log_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) {
  520.         fprintf (stderr,
  521.              "httpd: could not open transfer log file %s.\n", fname);
  522.         perror("open");
  523.         exit(1);
  524.     }
  525.     }
  526.  
  527.     return cls;
  528. }
  529.  
  530. void init_config_log (server_rec *s, pool *p)
  531. {
  532.     /* First, do "physical" server, which gets default log fd and format
  533.      * for the virtual servers, if they don't override...
  534.      */
  535.     
  536.     config_log_state *default_conf = open_config_log (s, p, NULL);
  537.     
  538.     /* Then, virtual servers */
  539.     
  540.     for (s = s->next; s; s = s->next) open_config_log (s, p, default_conf);
  541. }
  542.  
  543. module config_log_module = {
  544.    STANDARD_MODULE_STUFF,
  545.    init_config_log,        /* initializer */
  546.    NULL,            /* create per-dir config */
  547.    NULL,            /* merge per-dir config */
  548.    make_config_log_state,    /* server config */
  549.    NULL,            /* merge server config */
  550.    config_log_cmds,        /* command table */
  551.    NULL,            /* handlers */
  552.    NULL,            /* filename translation */
  553.    NULL,            /* check_user_id */
  554.    NULL,            /* check auth */
  555.    NULL,            /* check access */
  556.    NULL,            /* type_checker */
  557.    NULL,            /* fixups */
  558.    config_log_transaction    /* logger */
  559. };
  560.